/*-------------------------------- Arctic Core ------------------------------
 * Copyright (C) 2013, ArcCore AB, Sweden, www.arccore.com.
 * Contact: <contact@arccore.com>
 * 
 * You may ONLY use this file:
 * 1)if you have a valid commercial ArcCore license and then in accordance with  
 * the terms contained in the written license agreement between you and ArcCore, 
 * or alternatively
 * 2)if you follow the terms found in GNU General Public License version 2 as 
 * published by the Free Software Foundation and appearing in the file 
 * LICENSE.GPL included in the packaging of this file or here 
 * <http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt>
 *-------------------------------- Arctic Core -----------------------------*/



/*
 *  General requirements
 */
/** @req DCM600 */ // Generated by BSW builder


#include <string.h>
#include "Dcm.h"
#include "Dcm_Internal.h"
//#include "MemMap.h"

typedef struct {
	const PduInfoType 				*pduRxData;
	PduInfoType 					*pduTxData;
	const Dcm_DsdServiceTableType	*serviceTable;
	Dcm_ProtocolAddrTypeType		addrType;
	PduIdType 						txPduId;
	PduIdType 						rxPduId;
} MsgDataType;


// In "queue" to DSD
static boolean	dsdDslDataIndication = FALSE;
static MsgDataType msgData;

uint8	currentSid;
static boolean	suppressPosRspMsg;

static void createAndSendNcr(Dcm_NegativeResponseCodeType responseCode)
{
	if (!((msgData.addrType == DCM_PROTOCOL_FUNCTIONAL_ADDR_TYPE)
		  && ((responseCode == DCM_E_SERVICENOTSUPPORTED) || (responseCode == DCM_E_SUBFUNCTIONNOTSUPPORTED) || (responseCode == DCM_E_REQUESTOUTOFRANGE)))) {   /** @req DCM001 */
		msgData.pduTxData->SduDataPtr[0] = SID_NEGATIVE_RESPONSE;
		msgData.pduTxData->SduDataPtr[1] = currentSid;
		msgData.pduTxData->SduDataPtr[2] = responseCode;
		msgData.pduTxData->SduLength = 3;
		DslDsdProcessingDone(msgData.rxPduId, DSD_TX_RESPONSE_READY);	/** @req DCM114 */ /** @req DCM232.Ncr */
	}
	else {
		DslDsdProcessingDone(msgData.rxPduId, DSD_TX_RESPONSE_SUPPRESSED);
	}
}


static void selectServiceFunction(uint8 sid)
{
    /** @req DCM442.Partially */
    switch (sid)	 /** @req DCM221 */
    {
#ifdef DCM_USE_SERVICE_DIAGNOSTIC_SESSION_CONTROL
    case SID_DIAGNOSTIC_SESSION_CONTROL:
        DspUdsDiagnosticSessionControl(msgData.pduRxData, msgData.txPduId, msgData.pduTxData,msgData.addrType);
        break;
#endif

#ifdef DCM_USE_SERVICE_ECU_RESET
    case SID_ECU_RESET:
        DspUdsEcuReset(msgData.pduRxData, msgData.txPduId, msgData.pduTxData,msgData.addrType);
        break;
#endif

#ifdef DCM_USE_SERVICE_READ_DATA_BY_IDENTIFIER
    case SID_READ_DATA_BY_IDENTIFIER:
        DspUdsReadDataByIdentifier(msgData.pduRxData, msgData.pduTxData);
        break;
#endif


#ifdef DCM_USE_SERVICE_SECURITY_ACCESS
    case SID_SECURITY_ACCESS:
        DspUdsSecurityAccess(msgData.pduRxData, msgData.pduTxData,msgData.addrType);
        break;
#endif

#ifdef DCM_USE_SERVICE_WRITE_DATA_BY_IDENTIFIER
    case SID_WRITE_DATA_BY_IDENTIFIER:
        DspUdsWriteDataByIdentifier(msgData.pduRxData, msgData.pduTxData,msgData.addrType);
        break;
#endif


#ifdef DCM_USE_SERVICE_TESTER_PRESENT
    case SID_TESTER_PRESENT:
        DspUdsTesterPresent(msgData.pduRxData, msgData.pduTxData,msgData.addrType);
        break;
#endif

#ifdef DCM_USE_SERVICE_ROUTINE_CONTROL
    case SID_ROUTINE_CONTROL:
        DspUdsRoutineControl(msgData.pduRxData, msgData.pduTxData,msgData.addrType);
        break;
#endif

#ifdef DCM_USE_SERVICE_CONTROL_DTC_SETTING
    case SID_CONTROL_DTC_SETTING:
        DspUdsControlDtcSetting(msgData.pduRxData, msgData.pduTxData,msgData.addrType);
        break;
#endif


#ifdef DCM_USE_SERVICE_INPUT_OUTPUT_CONTROL_BY_IDENTIFIER
    case SID_INPUT_OUTPUT_CONTROL_BY_IDENTIFIER:
        DspIOControlByDataIdentifier(msgData.pduRxData, msgData.pduTxData, msgData.addrType);
        break;
#endif

#ifdef DCM_USE_SERVICE_COMMUNICATION_CONTROL
    case SID_COMMUNICATION_CONTROL:
        DspCommunicationControl(msgData.pduRxData, msgData.pduTxData, msgData.addrType);
        break;
#endif
#ifdef DCM_USE_SERVICE_REQUEST_DOWNLOAD
    case SID_REQUEST_DOWNLOAD:
        DspUdsRequestDownload(msgData.pduRxData, msgData.pduTxData,msgData.addrType);
        break;
#endif
#ifdef DCM_USE_SERVICE_TRANSFER_DATA
    case SID_TRANSFER_DATA:
        DspUdsTransferData(msgData.pduRxData, msgData.pduTxData,msgData.addrType);
        break;
#endif
#ifdef DCM_USE_SERVICE_REQUEST_TRANSFER_EXIT
    case SID_REQUEST_TRANSFER_EXIT:
        DspUdsRequestTransferExit(msgData.pduRxData, msgData.pduTxData,msgData.addrType);
        break;
#endif
    default:
        /* Non implemented service */
        createAndSendNcr(DCM_E_SERVICENOTSUPPORTED);
        break;
    }
}


static boolean lookupSid(uint8 sid, const Dcm_DsdServiceType **sidPtr)
{
	boolean returnStatus = TRUE;
	const Dcm_DsdServiceType *service = msgData.serviceTable->DsdService;

	while ((service->DsdSidTabServiceId != sid) && (!service->Arc_EOL)) {
		service++;
	}

	if (!service->Arc_EOL) {
		*sidPtr = service;
	}
	else {
		returnStatus = FALSE;
		*sidPtr = NULL;
	}

	return returnStatus;
}


/*
 * Exported functions
 */

void DsdInit(void)
{

}


void DsdMain(void)
{
	if (dsdDslDataIndication) {
		dsdDslDataIndication = FALSE;
		DsdHandleRequest();
	}
}

void DsdHandleRequest(void)
{
    const Dcm_DsdServiceType *sidConfPtr = NULL;

    if( msgData.pduRxData->SduLength > 0 ) {
    currentSid = msgData.pduRxData->SduDataPtr[0];	/** @req DCM198 */
    }

    if ( 0 == msgData.pduRxData->SduLength) {
        DslDsdProcessingDone(msgData.rxPduId, DSD_TX_RESPONSE_SUPPRESSED);
    }
	else if ((DCM_RESPOND_ALL_REQUEST == STD_ON) || ((currentSid & 0x7Fu) < 0x40)) {
        if (lookupSid(currentSid, &sidConfPtr))
		{
			if(currentSid == 0x34){
				currentSid = currentSid;
			}
            // SID found!
            if (DspCheckSessionLevel(sidConfPtr->DsdSidTabSessionLevelRef)) 
			{
                    if ( (sidConfPtr->DsdSidTabSubfuncAvail) && 
                         (msgData.pduRxData->SduDataPtr[1] & SUPPRESS_POS_RESP_BIT) ) 	/** @req DCM204 */
					{
                        suppressPosRspMsg = TRUE;
                        msgData.pduRxData->SduDataPtr[1] &= ~SUPPRESS_POS_RESP_BIT;	
                    }
                    else 
					{
                        suppressPosRspMsg = FALSE;
                    }
                    selectServiceFunction(currentSid);
            }
            else 
			{
				switch(currentSid)
				{
					case SID_ECU_RESET:
					case SID_CONTROL_DTC_SETTING:
					case SID_COMMUNICATION_CONTROL:

					if(msgData.addrType == DCM_PROTOCOL_PHYSICAL_ADDR_TYPE)
					{
						createAndSendNcr(DCM_E_SERVICENOTSUPPORTEDINACTIVESESSION);
					}
					else
					{				
						DslDsdProcessingDone(msgData.rxPduId, DSD_TX_RESPONSE_SUPPRESSED);
					}
					break;
					default:
						createAndSendNcr(DCM_E_SERVICENOTSUPPORTEDINACTIVESESSION);						
					break;
				}
            }
        }
        else 
		{
            createAndSendNcr(DCM_E_SERVICENOTSUPPORTED);
        }
    }
    else {
        // Inform DSL that message has been discard
        DslDsdProcessingDone(msgData.rxPduId, DSD_TX_RESPONSE_SUPPRESSED);
    }
}



void DsdDspProcessingDone(Dcm_NegativeResponseCodeType responseCode)
{
	if (responseCode == DCM_E_POSITIVERESPONSE) {
		if (!suppressPosRspMsg) {
			msgData.pduTxData->SduDataPtr[0] = currentSid | SID_RESPONSE_BIT;
			DslDsdProcessingDone(msgData.rxPduId, DSD_TX_RESPONSE_READY);
		}
		else {
			DspDcmConfirmation(msgData.txPduId);
			DslDsdProcessingDone(msgData.rxPduId, DSD_TX_RESPONSE_SUPPRESSED);
		}
	}
	else {
		createAndSendNcr(responseCode);
	}

}


void DsdDataConfirmation(PduIdType confirmPduId, NotifResultType result)
{
	DspDcmConfirmation(confirmPduId);

	(void)result;
}


void DsdDslDataIndication(const PduInfoType *pduRxData, const Dcm_DsdServiceTableType *protocolSIDTable, Dcm_ProtocolAddrTypeType addrType, PduIdType txPduId, PduInfoType *pduTxData, PduIdType rxContextPduId)
{
	msgData.rxPduId = rxContextPduId;
	msgData.txPduId = txPduId;
	msgData.pduRxData = pduRxData;
	msgData.pduTxData = pduTxData;
	msgData.addrType = addrType;
	msgData.serviceTable = protocolSIDTable;

	dsdDslDataIndication = TRUE;
}


